GLuint g_programObject;

struct Material {
	vec4 Ka;
	vec4 Kd;
	vec4 Ks;
};
struct Vertex {
	vec3 locationPos;
	vec3 normal;
	vec2 texturePos;
};
struct Texture {
	GLuint id;
	string type;
	string path;
};

class Mesh {
public:
	vector<Vertex> vertices;
	vector<Texture> textures;
	vector<unsigned int> indices;
	Material mater;
	GLuint VAO, VBO, EBO, bindingPoint, bindingBuffer;
	Mesh(vector<Vertex> vertices, vector<unsigned int>indices, vector<Texture> textures, Material mat)
	{
		this->indices = indices;
		this->vertices = vertices;
		this->textures = textures;
		this->mater = mat;
		setupMesh();
	}
	void setupMesh()
	{
		GLfloat lightData[] = {
			mater.Ka.x,mater.Ka.y,mater.Ka.z,1.0f,
			mater.Kd.x,mater.Kd.y,mater.Kd.z,1.0f,
			mater.Ks.x,mater.Ks.y,mater.Ks.z,1.0f,
		};
		GLint size;
		bindingPoint = 1;
		GLint locationPos, normal, texturePos, blockId;
		locationPos = glGetAttribLocation(g_programObject, "vPosition");
		texturePos = glGetAttribLocation(g_programObject, "texPosition");
		normal = glGetAttribLocation(g_programObject, "normal");
		blockId = glGetAttribLocation(g_programObject, "Light");
		glGenVertexArrays(1, &VAO);
		glGenBuffers(1, &VBO);
		glGenBuffers(1, &EBO);
		glGenBuffers(1, &bindingBuffer);

		glUniformBlockBinding(g_programObject, blockId, bindingPoint);
		glGetActiveUniformBlockiv(g_programObject, blockId, GL_UNIFORM_BLOCK_DATA_SIZE, &size);
		glBindBuffer(GL_UNIFORM_BUFFER, bindingBuffer);
		glBufferData(GL_UNIFORM_BUFFER, size, lightData, GL_DYNAMIC_DRAW);
		printf("block size:%d\n", size);
		glBindBufferBase(GL_UNIFORM_BUFFER, bindingPoint, bindingBuffer);

		glBindVertexArray(VAO);
		glBindBuffer(GL_ARRAY_BUFFER, VBO);
		glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex), &vertices[0], GL_STATIC_DRAW);

		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
		glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), &indices[0], GL_STATIC_DRAW);
		glEnableVertexAttribArray(locationPos);
		glVertexAttribPointer(locationPos, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0);
		glEnableVertexAttribArray(texturePos);
		glVertexAttribPointer(texturePos, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, texturePos));
		glEnableVertexAttribArray(normal);
		glVertexAttribPointer(normal, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, normal));
		glBindVertexArray(0);
	}
	void draw()
	{
		for (unsigned int i = 0; i < textures.size(); i++)
		{
			if (textures[i].type == "diffuse") {
				glActiveTexture(GL_TEXTURE1);
				glBindTexture(GL_TEXTURE_2D, textures[i].id);
				glUniform1i(glGetUniformLocation(g_programObject, "texId"), 1);
				break;
			}
		}

		// draw mesh
		glBindVertexArray(VAO);
		glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0);
		glBindVertexArray(0);
		glActiveTexture(GL_TEXTURE0);
	}
};
class Model {

#ifdef ZTN_DEBUG
	int chance;
	int mes;
#endif
public:
	int num;
	vector<Mesh> meshes;
	vector<Texture> loadedTextures;
	Model();

	void draw()
	{
#ifdef ZTN_DEBUG
		chance++;
		if (chance == 50)
		{
			chance = 0;
			mes += 1;
			if (mes == meshes.size())
			{
				mes = 0;
			}
			LOGD("mesh[%d]", mes);
		}
		meshes[mes].draw();
#else
		for (int i = 0; i<meshes.size(); i++)
		{
			meshes[i].draw();
		}

#endif
	}
	void processNode(aiNode* node, const aiScene* scene)
	{
		for (int i = 0; i<node->mNumMeshes; i++)
		{
			num++;
			aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
			meshes.push_back(processMesh(mesh, scene));

		}
		for (int i = 0; i<node->mNumChildren; i++)
		{
			processNode(node->mChildren[i], scene);
		}
	}
	void loadModel(const char* src)
	{
		Importer importer;
		const aiScene* scene = importer.ReadFile(src, aiProcess_Triangulate | aiProcess_FlipUVs);
		if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode)
		{
			printf("error%s\n", importer.GetErrorString());
			return;
		}
		processNode(scene->mRootNode, scene);
	}
	Mesh processMesh(aiMesh* mesh, const aiScene* scene)
	{
		vector<Vertex> vertices;
		vector<unsigned int> indices;
		vector<Texture> textures;
		Material mate;
		vec3 bufVertex;
		for (int i = 0; i<mesh->mNumVertices; i++)
		{
			Vertex vertex;
			vertex.locationPos.x = mesh->mVertices[i].x;
			vertex.locationPos.y = mesh->mVertices[i].y;
			vertex.locationPos.z = mesh->mVertices[i].z;

			vertex.normal.x = mesh->mNormals[i].x;
			vertex.normal.y = mesh->mNormals[i].y;
			vertex.normal.z = mesh->mNormals[i].z;

			if (mesh->mTextureCoords[0])
			{
				vertex.texturePos.x = mesh->mTextureCoords[0][i].x;
				vertex.texturePos.y = mesh->mTextureCoords[0][i].y;
			}
			else {
				vertex.texturePos.x = 0.0f;
				vertex.texturePos.y = 0.0f;
			}
			vertices.push_back(vertex);
		}

		for (unsigned int i = 0; i < mesh->mNumFaces; i++)
		{
			aiFace face = mesh->mFaces[i];
			for (unsigned int j = 0; j < face.mNumIndices; j++)
				indices.push_back(face.mIndices[j]);
		}
		aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex];
		aiColor3D color3D;
		material->Get(AI_MATKEY_COLOR_AMBIENT, color3D);
		mate.Ka = vec4(color3D.r, color3D.g, color3D.b, 1.0f);
		material->Get(AI_MATKEY_COLOR_DIFFUSE, color3D);
		mate.Kd = vec4(color3D.r, color3D.g, color3D.b, 1.0f);
		material->Get(AI_MATKEY_COLOR_SPECULAR, color3D);
		mate.Ks = vec4(color3D.r, color3D.g, color3D.b, 1.0f);

		vector<Texture> diffuse = loadTexture(aiTextureType_DIFFUSE, material, "diffuse");

		textures.insert(textures.end(), diffuse.begin(), diffuse.end());


		return Mesh(vertices, indices, textures, mate);
	}
	vector<Texture> loadTexture(aiTextureType type, aiMaterial* material, string typeName)
	{
		vector<Texture> textures;
		for (int i = 0; i<material->GetTextureCount(type); i++)
		{
			aiString pBuffer;
			material->GetTexture(type, i, &pBuffer);

			bool skip = false;
			for (int j = 0; j < loadedTextures.size(); j++) {
				if (!strcmp(loadedTextures[j].path.c_str(), pBuffer.C_Str())) {
					textures.push_back(loadedTextures[j]);
					skip = true;
					break;
				}
			}
			if (!skip)
			{
				Texture tex;
				tex.id = textureFromFile(pBuffer.C_Str());
				tex.path = string(pBuffer.C_Str());
				tex.type = typeName;
				loadedTextures.push_back(tex);
				textures.push_back(tex);
			}
		}
		return textures;

	}
	GLuint textureFromFile(const char* src)
	{
		string filename = string(src);
		GLuint textureId = 0;
		glGenTextures(1, &textureId);

		int width, height, n;
		unsigned char* data = stbi_load(filename.c_str(), &width, &height, &n, 0);
		if (data)
		{
			GLenum format;
			switch (n)
			{
			case 1:
				format = GL_RED;
				break;
			case 3:
				format = GL_RGB;
				break;
			case 4:
				format = GL_RGBA;
				break;
			default:
				printf("unknown format!!\n");
				format = GL_RGB;
				break;
			}
			glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
			glBindTexture(GL_TEXTURE_2D, textureId);
			glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
			glGenerateMipmap(GL_TEXTURE_2D);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
			stbi_image_free(data);
		}
		else {
			printf("can't load file:%s\n", filename.c_str());
		}
		printf("%u\n", textureId);
		glBindTexture(GL_TEXTURE_2D, 0);
		return textureId;
	}
};
Model::Model()
{
#ifdef ZTN_DEBUG
	this->chance = 0;
	this->mes = 0;
#endif
}